import os, locale, yaml, json, datetime
from rest_framework import response, status
from httprunner.api import HttpRunner
from httprunner.report import gen_html_report
from apps.api.function.models import FunctionModel
from apps.api.config.models import ConfigModel
from apps.api.project.models import ProjectModel
from apps.api.interface.models import InterfaceModel
from apps.api.testcase.models import TestCaseModel
from apps.product.report.models import ReportModel
from base.settings import REPORT_FILE_PATH, LOG_FILE_PATH
from utils.response import message_response


def date_format():
    """允许格式化中文格式的日期"""
    locale.setlocale(locale.LC_CTYPE, "chinese")
    return "%Y-%m-%d %H:%M:%S"


def change_data_type(data_type, data):
    if data_type == "int":
        return int(data)
    elif data_type == "float":
        return float(data)
    elif data_type == "boolean":
        if data in ["true", "True", "TRUE"]:
            return True
        elif data in ["false", "False", "FALSE"]:
            return False
        else:
            return bool(data)
    else:
        return str(data)


def list_to_dict(variables):
    """变量格式转换"""
    result = {}
    for variable in variables:
        result[variable["key"]] = change_data_type(variable["type"], variable["value"])
    return result


def list_to_dict_list(variables):
    """变量格式转换"""
    result = []
    for variable in variables:
        result.append({variable["key"]: change_data_type(variable["type"], variable["value"])})
    return result


def validate_list_to_dict(validates):
    """断言格式转换"""
    result = []
    for validate in validates:
        valid = dict()
        valid[validate["method"]] = [validate["key"], change_data_type(validate["type"], validate["value"])]
        result.append(valid)
    return result


def case_content_change(content):
    """用例请求体转换"""
    if "params" in content["request"].keys():
        if len(content["request"]["params"]) != 0:
            content["request"]["params"] = list_to_dict(content["request"]["params"])
    if "data" in content["request"].keys():
        if len(content["request"]["data"]) != 0:
            content["request"]["data"] = list_to_dict(content["request"]["data"])
    if len(content["variables"]) != 0:
        content["variables"] = list_to_dict_list(content["variables"])
    if len(content["validate"]) != 0:
        content["validate"] = validate_list_to_dict(content["validate"])
    return content


def assemble_case(basic_path, env_obj, case_obj):
    """组装用例，转成yaml格式"""
    ready_to_run_case = []
    # 初始化用例配置信息
    config = {
        "config": {
            "name": case_obj.name,
            "base_url": env_obj.url if env_obj else "",
        }
    }
    # 添加配置到用例中
    ready_to_run_case.append(config)

    # 创建项目名称文件夹，并在该目录下添加.env文件和debugtalk.py文件
    project_id = InterfaceModel.objects.filter(id=case_obj.interface_id).filter(is_delete=0).first().project_id
    project_name = ProjectModel.objects.filter(id=project_id).filter(is_delete=0).first().name
    project_path = os.path.join(basic_path, project_name)
    if not os.path.exists(project_path):
        os.makedirs(project_path)
    func_obj = FunctionModel.objects.filter(project_id=project_id).filter(is_delete=0).first()
    func_content = func_obj.content if func_obj else ""
    func_path = os.path.join(project_path, "debugtalk.py")
    if not os.path.exists(func_path):
        with open(file=func_path, mode="w", encoding="utf-8") as f:
            f.write(func_content)

    # 创建接口名称文件夹
    interface_path = os.path.join(project_path, case_obj.interface.name)
    if not os.path.exists(interface_path):
        os.makedirs(interface_path)

    # 根据情况更新用例配置
    if case_obj.config:
        conf_id = case_obj.config
        conf_obj = ConfigModel.objects.filter(id=conf_id).filter(is_delete=0).first()
        if conf_obj:
            content = eval(conf_obj.content)
            new_config = {
                "config": {
                    "name": case_obj.name,
                    "base_url": env_obj.url if env_obj else "",
                    "request": {
                        "headers": content["headers"] if len(content["headers"]) != 0 else {}
                    },
                    "variables": list_to_dict_list(content["variables"]) if len(content["variables"]) != 0 else []
                }
            }
            ready_to_run_case[0] = new_config

    # 添加前置用例
    if case_obj.fixture != "[]":
        for prev_case_id in eval(case_obj.fixture):
            prev_case = TestCaseModel.objects.filter(id=prev_case_id).filter(is_delete=0).first()
            prev_case_content = eval(prev_case.content)
            prev_case_content = case_content_change(prev_case_content)
            prev_case_content["name"] = prev_case.name
            ready_to_run_case.append({"test": prev_case_content})

    # 添加当前用例
    current_case = eval(case_obj.content)
    current_case = case_content_change(current_case)
    current_case["name"] = case_obj.name
    ready_to_run_case.append({"test": current_case})

    # 转成yml格式文件
    with open(file=os.path.join(interface_path, case_obj.name + ".yml"), mode="w", encoding="utf-8") as f:
        yaml.dump(data=ready_to_run_case, stream=f, default_flow_style=False, encoding='utf-8', allow_unicode=True)


def run_case(name, path):
    """执行用例"""
    runner = HttpRunner(log_level="DEBUG", log_file=os.path.join(LOG_FILE_PATH, "run_case_{}.log".format(datetime.datetime.strftime(datetime.datetime.now(), "%Y%m%d%H%M%S"))))
    try:
        summary = runner.run(path)
    except Exception as e:
        message = {"run": "运行结束，请前往报告管理模块查看详情！"}
        result = message_response(code=1, message=message)
        return response.Response(result, status=status.HTTP_200_OK)
    # 创建报告
    create_report(summary, name)
    # 响应
    message = {"run": "运行结束，请前往报告管理模块查看详情！"}
    result = message_response(code=0, message=message)
    return response.Response(result, status=status.HTTP_200_OK)


def create_report(summary, report_name=None):
    # 运行开始时间
    time_stamp = int(summary["time"]["start_at"])
    start_datetime = datetime.datetime.fromtimestamp(time_stamp).strftime('%Y-%m-%d %H:%M:%S')
    summary['time']['start_datetime'] = start_datetime

    # 运行时间保留3位小数
    summary['time']['duration'] = round(summary['time']['duration'], 3)

    # 保存报告名称
    report_name = report_name if report_name else start_datetime
    summary['html_report_name'] = report_name

    # 遍历用例集
    for item in summary['details']:
        # 对时间戳进行处理
        try:
            time_stamp = int(item['time']['start_at'])
            item['time']['start_at'] = datetime.datetime.fromtimestamp(time_stamp).strftime('%Y-%m-%d %H:%M:%S')
        except Exception:
            pass

        try:
            # 遍历用例
            for record in item['records']:
                # 对时间戳进行处理
                try:
                    time_stamp = int(record['meta_data']['request']['start_timestamp'])
                    record['meta_data']['request']['start_timestamp'] = \
                        datetime.datetime.fromtimestamp(time_stamp).strftime('%Y-%m-%d %H:%M:%S')
                except Exception:
                    pass

                record['meta_data']['response']['content'] = record['meta_data']['response']['content']. \
                    decode('utf-8')
                record['meta_data']['response']['cookies'] = dict(record['meta_data']['response']['cookies'])

                request_body = record['meta_data']['request']['body']
                if isinstance(request_body, bytes):
                    record['meta_data']['request']['body'] = request_body.decode('utf-8')
        except Exception as e:
            continue

    # 报告详细信息
    # summary = json.dumps(summary, ensure_ascii=False)
    report_name = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d%H%M%S') + '_' + report_name
    report_path = gen_html_report(summary=summary, report_file=os.path.join(REPORT_FILE_PATH, report_name + ".html"))

    # 生成html
    with open(report_path, "r", encoding='utf-8') as stream:
        report_content = stream.read()

    test_report = {
        'name': report_name,
        'result': 0 if summary.get('success') else 1,
        'success': summary.get('stat').get('teststeps').get('successes'),
        'fail': summary.get('stat').get('teststeps').get('failures'),
        'error': summary.get('stat').get('teststeps').get('errors'),
        'skip': summary.get('stat').get('teststeps').get('skipped'),
        'count': summary.get('stat').get('teststeps').get('total'),
        'path': report_path,
        'content': report_content,
        'summary': json.dumps(summary, ensure_ascii=False)
    }
    ReportModel.objects.create(**test_report)


def get_report_content(filename, chunk_size=1024):
    with open(file=filename, encoding="utf-8") as f:
        while True:
            content = f.read(chunk_size)
            if not content:
                break
            yield content
